package com.gcd.alienandwheel.actor;

import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.scenes.scene2d.Group;
import com.gcd.alienandwheel.MyUtil;

/**
 *
 * @author Rezan Achmad
 */
public class Tyro extends Group {

    public class Wheel extends MyActor {

        private final static float FRAME_DURATION_LOW = 0.1f;
        private final static float FRAME_DURATION_HIGH = 0.01f;
        TextureRegion[] keyFrames;
        float frameDuration;
        float stateTime = 0;
        boolean looping = true;

        public Wheel(String name) {
            super(name);
            setTexture(MyUtil.loadImage("tyro/1.png"));
            setAnimation(FRAME_DURATION_LOW,
                    new TextureRegion(new Texture(MyUtil.loadImage("tyro/1.png"))),
                    new TextureRegion(new Texture(MyUtil.loadImage("tyro/2.png"))),
                    new TextureRegion(new Texture(MyUtil.loadImage("tyro/3.png"))),
                    new TextureRegion(new Texture(MyUtil.loadImage("tyro/4.png"))),
                    new TextureRegion(new Texture(MyUtil.loadImage("tyro/5.png"))));
        }

        /**
         * Draw Tyro.
         */
        protected void draw(SpriteBatch batch, float parentAlpha) {
            batch.draw(getKeyFrame(stateTime, looping), x, y, originX, originY, width, height, scaleX, scaleY, rotation);
        }

        @Override
        protected void act(float delta) {
            updateAnimation(delta);
        }

        /**
         * Update Tyro animate or not. And also update frame duration.
         */
        private void updateAnimation(float delta) {
            // Update total time
            stateTime += delta;

            // Animate or not
            if (speedForward == 0) {
                looping = false;
            } else {
                looping = true;
            }

            // Update Frame Duration
            frameDuration = FRAME_DURATION_LOW + (FRAME_DURATION_HIGH - FRAME_DURATION_LOW) * speedForward / maxSpeed;
        }

        /**
         * Returns a {@link TextureRegion} based on the so called state time. 
         * This is the amount of seconds an object has spent in the state this
         * Animation instance represents, e.g. running, jumping and so on. The
         * mode specifies whether the animation is looping or not. 
         * @param stateTime the time spent in the state represented by this animation.
         * @param looping whether the animation is looping or not.
         * @return the TextureRegion representing the frame of animation for the given state time.
         */
        public TextureRegion getKeyFrame(float stateTime, boolean looping) {
            int frameNumber = (int) (stateTime / frameDuration);

            if (!looping) {
                frameNumber = Math.min(keyFrames.length - 1, frameNumber);
            } else {
                if (speedForward < 0) {
                    frameNumber = frameNumber % keyFrames.length;
                } else {
                    frameNumber = keyFrames.length - 1 - (frameNumber % keyFrames.length);
                }
            }
            return keyFrames[frameNumber];
        }

        /**
         * Set frame duration and key frames.
         * @param frameDuration
         * @param keyFrames All of key frames.
         */
        private void setAnimation(float frameDuration, TextureRegion... keyFrames) {
            this.frameDuration = frameDuration;
            this.keyFrames = keyFrames;
        }
    }
    public float speedForward = 0;
    public float speedDecay = 0.99f;
    public float acc = 200;
    public float maxSpeed = 350;
    public float maxSpeedOnLand = 50;
    public float rotInc = 75;
    public boolean onTheLand = false;
    public Bar velocityBar;
    public Wheel wheel;

    public Tyro(String name) {
        super(name);
        width = 128;
        height = 128;
        originX = width / 2;
        originY = height / 2;

        wheel = new Wheel("wheel");
        velocityBar = new Bar("velocityBar", maxSpeed, 10, 30, Color.WHITE, false);
        velocityBar.x = width - velocityBar.width;
        velocityBar.y = height - velocityBar.height;

        addActor(wheel);
        addActor(velocityBar);
    }

    /**
     * Act of Tyro. All of logic should be here.
     */
    protected void act(float delta) {
        boundTheSpeed();

        updatePosition(delta);

        decreaseSpeed(delta);

        velocityBar.setValue(Math.abs(speedForward));

        super.act(delta);
    }

    /**
     * Decrease speed of vehicle because of friction.
     * @param delta Delta time
     */
    private void decreaseSpeed(float delta) {
        if (Math.abs(speedForward) > acc * delta) {
            speedForward *= speedDecay;
        } else {
            speedForward = 0;
        }
    }

    /*
     * Update x and y.
     */
    private void updatePosition(float delta) {
        Vector2 v = new Vector2(0, speedForward);
        v.rotate(rotation);
        x += (v.x * delta);
        y += (v.y * delta);
    }

    /*
     * Bound the speed. If current speed grater that maximum speed, set current
     * speed to maximum speed.
     */
    private void boundTheSpeed() {
        if (onTheLand) {
            if (Math.abs(speedForward) > maxSpeedOnLand) {
                if (speedForward < 0) {
                    speedForward = -maxSpeedOnLand;
                } else {
                    speedForward = maxSpeedOnLand;
                }
            }
        } else {
            // Vehicle in the road
            if (Math.abs(speedForward) > maxSpeed) {
                if (speedForward < 0) {
                    speedForward = -maxSpeed;
                } else {
                    speedForward = maxSpeed;
                }
            }
        }
    }
}
